package org.phalanx.map.xml;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import org.phalanx.digest.MessageDigestManager;
import org.phalanx.map.MapCoordinates;
import org.phalanx.map.MapTile;
import org.phalanx.map.MapTileSet;
import org.phalanx.map.PhalanxMap;
import org.phalanx.map.PhalanxTileSetReader;
import org.phalanx.zip.ZipFileArchiveReader;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class MapXMLFileHandler extends DefaultHandler {
	private PhalanxMap map;
	private MapTileSet tileSet;
	private boolean isTagClosed = true;
	private boolean isPhalanxMapScope;
	private boolean isMapperScope;
	private boolean metaDataScope = false;
	private String currentContextTag;
	private String cacheDirectory;
	
	
	public MapXMLFileHandler(PhalanxMap newMap, MapTileSet newSet, String cacheDirectory){
		this.map = newMap;
		this.tileSet = newSet; // This should always be the default tile set until overridden
		this.cacheDirectory = cacheDirectory;
	}
	
	@Override
	public void characters(char[] ch, int start, int length) throws SAXException {
		// TODO Auto-generated method stub
		String data = new String(ch, start, length);

		if (!isTagClosed) {
			if (this.metaDataScope) {
				if (currentContextTag.equals("Author")) {
					map.setAuthorName(data);
				}
				else if (currentContextTag.equals("Email")) {
					map.setAuthorEmail(data);
				}
				else if (currentContextTag.equals("Country")) {
					map.setAuthorCountry(data);
				}
				else if (currentContextTag.equals("AuthorURL")) {
					map.setAuthorURL(data);
				}
			}
		}
		super.characters(ch, start, length);
	}

	@Override
	public void endDocument() throws SAXException {
		// TODO Auto-generated method stub
		super.endDocument();
	}

	@Override
	public void endElement(String arg0, String arg1, String arg2)
			throws SAXException {
		// TODO Auto-generated method stub
		isTagClosed = true;
		
		if (currentContextTag.equals("MetaData")) {
			this.metaDataScope = false;
		}
		super.endElement(arg0, arg1, arg2);
	}

	@Override
	public void startDocument() throws SAXException {
		// TODO Auto-generated method stub
		super.startDocument();
	}

	@Override
	public void startElement(String arg0, String localName, String qName,
			Attributes attr) throws SAXException {
		isTagClosed = false;
		currentContextTag = localName;
		if (localName.equals("PhalanxMap")){
			this.isPhalanxMapScope = true;
			String mapName = attr.getValue("name");
			String version = attr.getValue("version");
			map.setMapName(mapName);
			map.setMapVersion(version);
		}
		
		else if (isPhalanxMapScope){
			if (localName.equals("MetaData")) {
				metaDataScope = true;
			}
			else if (localName.equals("ImportSet")){
				String tileSetName = attr.getValue("tileset");
				if (tileSetName != null && !tileSetName.equalsIgnoreCase("default")){
					// Treat is default
					loadTileSet(tileSetName);					
				}
				map.setMapTileSet(this.tileSet);
			}
			else if (localName.equals("Mapper")){
				this.isMapperScope = true;
				Integer width = isNumber(attr.getValue("width")) ? Integer.valueOf(attr.getValue("width")) : 0;
				Integer length = isNumber(attr.getValue("length")) ? Integer.valueOf(attr.getValue("length")) : 0;
				String defaultTile = attr.getValue("defaulttile");
				String tileSize = attr.getValue("TileSize");
				
				if (tileSize != null){
					Integer tileSizeInt = Integer.valueOf(tileSize);
					map.setTileSize(tileSizeInt);
				}
				map.setMapDimensions(length, width);
				MapTile thisTile =  tileSet.getMapTileByName(defaultTile.toLowerCase());

				if (thisTile != null){
					map.setDefaultMapTile(thisTile, true);
				}
			}
			else if (isMapperScope){
				if (localName.equals("Square")){
					Integer xValue = isNumber(attr.getValue("xvalue")) ? Integer.valueOf(attr.getValue("xvalue")) : null;
					Integer yValue = isNumber(attr.getValue("yvalue")) ? Integer.valueOf(attr.getValue("yvalue")) : null;
					String tileName = attr.getValue("tile").toLowerCase();
					
					if (xValue != null && yValue != null && tileName != null){
						MapTile thisTile = tileSet.getMapTileByName(tileName);
						
						if (thisTile != null){
							map.setMapTile(xValue, yValue, thisTile);
						}
					}
				}
				if (localName.equals("BuildPoint")){
					Integer xValue = isNumber(attr.getValue("xvalue")) ? Integer.valueOf(attr.getValue("xvalue")) : null;
					Integer yValue = isNumber(attr.getValue("yvalue")) ? Integer.valueOf(attr.getValue("yvalue")) : null;
					
					if (xValue != null && yValue != null){
						map.addBuildPoint(new MapCoordinates(xValue, yValue));
					}
				}
			}
		}
		
		super.startElement(arg0, localName, qName, attr);
	}
	
	private boolean isNumber(String strNum){
		boolean isNumber = false;

		if (strNum != null){
			try {
				Integer newInt = Integer.valueOf(strNum);
				isNumber = true;
			} catch (NumberFormatException nfe){}
		}
		return isNumber;
	}
	
	private void loadTileSet(String archiveFilePath){
/*		PhalanxTileSetReader r = new PhalanxTileSetReader();
		try {
			r.setTerrainFileByName(archiveFilePath);
			if (r.readInTerrain()){
				this.tileSet = r.getMapTileSet();
				tileSet.setTileSetArchiveFile(archiveFilePath);
			}
		} catch (IOException ie){}*/
		
		try {
			ZipFileArchiveReader z = new ZipFileArchiveReader(map.getMapFilePath());
			
			if (z.isFileExist()){
				byte[] tileSetFile = z.getArchivedFile(archiveFilePath);

				String md5String = MessageDigestManager.getMD5Hash(tileSetFile);
				createCachedTileSet(md5String, tileSetFile);
				String cachedFileName = cacheDirectory + "/" + md5String + ".zip";

				PhalanxTileSetReader r = new PhalanxTileSetReader();
				r.setTerrainFileByName(cachedFileName);

				if (r.readInTerrain()){
					this.tileSet = r.getMapTileSet();
					tileSet.setTileSetArchiveFile(cachedFileName);
					tileSet.setCanonicalFilePath(archiveFilePath);
				}

			}
		} catch (IOException ie){}
	}
	
	private boolean createCachedTileSet(String namePrefix, byte[] fileBytes){
		File cachedFile = new File(cacheDirectory + "/" + namePrefix + ".zip");
		boolean isSuccess = false;
		
		try {
		if (!cachedFile.exists()){
			FileOutputStream fOut = new FileOutputStream(cachedFile);
			fOut.write(fileBytes);
			isSuccess = true;
		}
		} catch (IOException ie){}
		
		return isSuccess;
	}

}
